#ifndef _M_MATCHER_H
#define _M_MATCHER_H

#include <list>
#include <mutex>
#include <condition_variable>

#include "util.hpp"
#include "online.hpp"
#include "db.hpp"
#include "room.hpp"

template <class T>
class match_queue
{
private:
    // 用链表而不用队列是因为我们可能需要删除数据
    std::list<T> _list;
    // 实现线程安全
    std::mutex _mutex;
    // 这个条件变量主要为了阻塞消费者，后边使用的时候：队列元素个数小于2则阻塞
    std::condition_variable _cond;

public:
    // 获取元素个数
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }

    // 判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }

    // 阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }

    // 入队数据，并唤醒线程
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }

    // 出队数据
    bool pop(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty() == true)
            return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }

    // 移除指定的数据
    void remove(T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }
};

class matcher
{
private:
    match_queue<uint64_t> _queue_bronze; // 青铜
    match_queue<uint64_t> _queue_silver; // 白银
    match_queue<uint64_t> _queue_gold;   // 黄金
    std::thread _th_b;
    std::thread _th_s;
    std::thread _th_g;
    room_manager *_rm;
    user_table *_ut;
    online_manager *_om;

private:
    void handle_match(match_queue<uint64_t> &mq)
    {
        while (1)
        {
            // 1.判断队列人数是否大于2
            while (mq.size() < 2)
                mq.wait();
            // 2.走到这一步代表人数够了
            uint64_t u1, u2;
            bool ret = mq.pop(u1);
            if (ret == false)
                continue;
            ret = mq.pop(u2);
            if (ret == false)
            {
                this->add(u1);
                continue;
            }

            // 3.校验两个玩家是否在线，如果有人掉线，则要把另一个人重新添加入队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(u1);
            if (conn1.get() == nullptr)
            {
                this->add(u2);
                continue;
            }

            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(u2);
            // if (conn1.get() == nullptr) bug???
            if (conn2.get() == nullptr)
            {
                this->add(u1);
                continue;
            }

            // 4.为两个玩家创建房间，并将玩家加入到房间中
            room_ptr rp = _rm->create_room(u1, u2);
            if (rp.get() == nullptr)
            {
                this->add(u1);
                this->add(u2);
                continue;
            }

            // 5.对两个玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
    }

    void th_b_entry() { return handle_match(_queue_bronze); }
    void th_s_entry() { return handle_match(_queue_silver); }
    void th_g_entry() { return handle_match(_queue_gold); }

public:
    matcher(room_manager *rm, user_table *ut, online_manager *om) : _rm(rm), _ut(ut), _om(om),
                                                                    _th_b(std::thread(&matcher::th_b_entry, this)),
                                                                    _th_s(std::thread(&matcher::th_s_entry, this)),
                                                                    _th_g(std::thread(&matcher::th_g_entry, this))
    {
        DBG_LOG("游戏匹配模块初始化完毕...");
    }

    bool add(uint64_t uid)
    {
        // 根据玩家的天梯分数，来判定玩家的档次，添加到不同的匹配队列
        // 1.根据用户id，获取玩家信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DBG_LOG("获取玩家:%d 信息失败！", uid);
            return false;
        }
        int score = user["score"].asInt();
        DBG_LOG("%d add in queue", uid);

        // 2.添加到指定队列
        if (score < 2000)
            _queue_bronze.push(uid);
        else if (score >= 2000 && score < 3000)
            _queue_silver.push(uid);
        else
            _queue_gold.push(uid);
        return true;
    }

    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DBG_LOG("获取玩家:%d 信息失败！", uid);
            return false;
        }
        int score = user["score"].asInt();
        if (score < 2000)
            _queue_bronze.remove(uid);
        else if (score >= 2000 && score < 3000)
            _queue_silver.remove(uid);
        else
            _queue_gold.remove(uid);
        return true;
    }
};

#endif
